import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;


public class Board {

	private Map<Cell, Piece> pieceLocations = new HashMap<Cell, Piece>();

	public boolean hasPieceOn(Cell cell) {
		return pieceOn(cell) != null;
	}

	public Piece pieceOn(Cell cell) {
		return pieceLocations.get(cell);
	}

	public void placePiece(Cell cell, Piece piece) {
		if (pieceLocations.get(cell) == null) pieceLocations.put(cell, piece);
		else throw new RuntimeException(cell.toString() + " is occupied.");
	}

	public void movePiece(Piece piece, Cell startCell, Cell endCell) {
		if (piece != null && piece.equals(pieceOn(startCell))) {
			pieceLocations.remove(startCell);
			pieceLocations.put(endCell, piece);
		}
	}

	public void removePiece(Cell cell) {
		if (pieceLocations.containsKey(cell)) {
			pieceLocations.remove(cell);
		}
	}

	public boolean clearPathBetween(Cell startCell, Cell endCell) {
		boolean clearPathExists = false;

		if (startCell.equals(endCell)) {
			clearPathExists = false;
		}
		else if (startCell.inSameFileAs(endCell)) {
			clearPathExists = clearPathInFile(startCell, endCell);
		}
		else if (startCell.inSameRankAs(endCell)) {
			clearPathExists = clearPathInRank(startCell, endCell);
		}
		else if (startCell.inDiagonalPathWith(endCell)) {
			clearPathExists = clearPathInDiagonal(startCell, endCell);
		}

		return clearPathExists;
	}

	/*public boolean inCheck(int owner) {
		return cellInCheck(findKingCell(owner), owner);
	}

	public boolean cellInCheck(Cell checkCell, int owner) {
		boolean inCheck = false;

		Set<Cell> cells = pieceLocations.keySet();
		for (Iterator<Cell> cellIter = cells.iterator(); cellIter.hasNext() && !inCheck; ) {
			Cell cell = cellIter.next();
			if (pieceOn(cell).getOwner() != owner) {
				Piece attackPiece = pieceOn(cell);
				if (attackPiece.canAttack(this, cell, checkCell)) {
					inCheck = true;
				}
			}
		}

		return inCheck;
	}

	private Cell findKingCell(int owner) {
		Cell kingCell = null;

		Set<Cell> cells = pieceLocations.keySet();
		for (Iterator<Cell> cellIter = cells.iterator(); cellIter.hasNext() && kingCell == null; ) {
			Cell cell = cellIter.next();
			Piece piece = pieceOn(cell);
			if (piece.getPieceType() == Piece.TYPE_KING && piece.getOwner() == owner) {
				kingCell = cell;
			}
		}

		return kingCell;
	}*/

	private boolean clearPathInRank(Cell startCell, Cell endCell) {
		boolean clearPathExists = true;

		Set<Cell> cellsBetween = new HashSet<Cell>();
		for (char file = 'a'; file <= 'h'; file++) {
			if (fileBetween(file, startCell.file(), endCell.file())) {
				cellsBetween.add(new Cell(file, startCell.rank()));
			}
		}

		for (Cell cell : cellsBetween) {
			if (hasPieceOn(cell)) {
				clearPathExists = false;
			}
		}

		return clearPathExists;
	}

	private boolean clearPathInFile(Cell startCell, Cell endCell) {
		boolean clearPathExists = true;

		Set<Cell> cellsBetween = new HashSet<Cell>();
		for (char rank = '1'; rank <= '8'; rank++) {
			if (rankBetween(rank, startCell.rank(), endCell.rank())) {
				cellsBetween.add(new Cell(startCell.file(), rank));
			}
		}

		for (Cell cell : cellsBetween) {
			if (hasPieceOn(cell)) {
				clearPathExists = false;
			}
		}

		return clearPathExists;
	}

	private boolean clearPathInDiagonal(Cell startCell, Cell endCell) {
		boolean clearPathExists = true;

		int rankAdjustment = (startCell.rank() < endCell.rank() ? 1 : -1);
		int fileAdjustment = (startCell.file() < endCell.file() ? 1 : -1);
		char rank = (char) (startCell.rank() + rankAdjustment);
		char file = (char) (startCell.file() + fileAdjustment);

		while (rank != endCell.rank()) {
			if (hasPieceOn(new Cell(file, rank))) {
				clearPathExists = false;
			}
			rank = (char) (rank + rankAdjustment);
			file = (char) (file + fileAdjustment);
		}

		return clearPathExists;
	}

	private boolean rankBetween(char rank, char startRank, char endRank) {
		if (rank > startRank && rank < endRank) return true;
		else if (rank > endRank && rank < startRank) return true;
		else return false;
	}

	private boolean fileBetween(char file, char startFile, char endFile) {
		if (file > startFile && file < endFile) return true;
		else if (file > endFile && file < startFile) return true;
		else return false;
	}
	
	public Map<Cell, Piece> getPieceLocations() {
		return pieceLocations;
	}

}
